﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using EnsureThat;

namespace Commons
{
	/// <summary>
	/// Extends various framework classes with new, useful, methods
	/// </summary>
	public static class Extensions
	{
		/// <summary>
		/// Applies an action to each element of a collection
		/// </summary>
		public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
		{
			Ensure.That(source, "source").IsNotNull();
			Ensure.That(action, "action").IsNotNull();
			foreach (var item in source)
				action(item);
		}

		/// <summary>
		/// Gets a value from this <see cref="SerializationInfo"/>
		/// </summary>
		public static T GetValue<T>(this SerializationInfo info, string name)
		{
			Ensure.That(info, "info").IsNotNull();
			Ensure.That(name, "name").IsNotNullOrWhiteSpace();
			return (T)info.GetValue(name, typeof(T));
		}

		/// <summary>
		/// Converts this <see cref="WaitHandle"/> to a task, with given <paramref name="timeout"/> (-1 for no timeout)
		/// </summary>
		public static Task<bool> ToTask(this WaitHandle handle, int timeout = -1)
		{
			Ensure.That(handle, "this").IsNotNull();
			Ensure.That(timeout, "timeout").IsGte(-1);
			TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
			ManualResetEventSlim registrationReady = new ManualResetEventSlim();
			RegisteredWaitHandle reg = null;
			reg = ThreadPool.RegisterWaitForSingleObject(handle, (_, timedOut) =>
			{
				registrationReady.Wait();
				reg.Unregister(handle);
				tcs.TrySetResult(!timedOut);
			}, null, timeout, true);
			registrationReady.Set();
			return tcs.Task;
		}
	}
}
